From 0600fb9a5769556273c6f8c83d0cc5e3fe775e0a Mon Sep 17 00:00:00 2001 From: =?utf8?q?T=C3=A9o=20Mazars?= Date: Mon, 24 Jun 2013 22:55:35 +0200 Subject: [PATCH] extensions: Add the HSL color space Needed at least as legacy --- extensions/HSL.c | 286 +++++++++++++++++++++++++++++++++++++++++ extensions/Makefile.am | 2 + tests/Makefile.am | 1 + tests/hsl.c | 111 ++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 extensions/HSL.c create mode 100644 tests/hsl.c diff --git a/extensions/HSL.c b/extensions/HSL.c new file mode 100644 index 0000000..5426474 --- /dev/null +++ b/extensions/HSL.c @@ -0,0 +1,286 @@ +/* babl - dynamically extendable universal pixel conversion library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see + * . + */ + +#include "config.h" + +#include +#include + +#include "babl.h" +#include "base/util.h" + +#define MIN(a,b) ((a > b) ? b : a) +#define MAX(a,b) ((a < b) ? b : a) + +static long rgba_to_hsla (char *src, + char *dst, + long samples); +static long hsla_to_rgba (char *src, + char *dst, + long samples); +static long rgba_to_hsl (char *src, + char *dst, + long samples); +static long hsl_to_rgba (char *src, + char *dst, + long samples); +static void rgb_to_hsl_step (double *src, + double *dst); +static void hsl_to_rgb_step (double *src, + double *dst); +static inline double hue2cpn (double p, + double q, + double hue); +int init (void); + + +int +init (void) +{ + babl_component_new ("hue", NULL); + babl_component_new ("saturation", NULL); + babl_component_new ("lightness", NULL); + babl_component_new ("alpha", NULL); + + babl_model_new ("name", "HSLA", + babl_component ("hue"), + babl_component ("saturation"), + babl_component ("lightness"), + babl_component ("alpha"), + NULL); + babl_model_new ("name", "HSL", + babl_component ("hue"), + babl_component ("saturation"), + babl_component ("lightness"), + NULL); + + + babl_conversion_new (babl_model ("RGBA"), + babl_model ("HSLA"), + "linear", rgba_to_hsla, + NULL); + babl_conversion_new (babl_model ("RGBA"), + babl_model ("HSL"), + "linear", rgba_to_hsl, + NULL); + babl_conversion_new (babl_model ("HSLA"), + babl_model ("RGBA"), + "linear", hsla_to_rgba, + NULL); + babl_conversion_new (babl_model ("HSL"), + babl_model ("RGBA"), + "linear", hsl_to_rgba, + NULL); + + babl_format_new ("name", "HSLA float", + babl_model ("HSLA"), + babl_type ("float"), + babl_component ("hue"), + babl_component ("saturation"), + babl_component ("lightness"), + babl_component ("alpha"), + NULL); + babl_format_new ("name", "HSL float", + babl_model ("HSL"), + babl_type ("float"), + babl_component ("hue"), + babl_component ("saturation"), + babl_component ("lightness"), + NULL); + return 0; +} + + +static inline void +rgb_to_hsl_step (double* src, + double* dst) +{ + + double min, max; + double hue, saturation, lightness; + int cpn_max; + + double red = linear_to_gamma_2_2 (src[0]); + double green = linear_to_gamma_2_2 (src[1]); + double blue = linear_to_gamma_2_2 (src[2]); + + max = MAX (red, MAX (green, blue)); + min = MIN (red, MIN (green, blue)); + + if (max == red) + cpn_max = 0; + else if (max == green) + cpn_max = 1; + else + cpn_max = 2; + + lightness = (max + min) / 2.0; + + if (max == min) + { + hue = saturation = 0; + } + else + { + double diff = max - min; + double sum = max + min; + saturation = lightness > 0.5 ? diff / (2.0 - sum) : diff / sum; + switch (cpn_max) + { + case 0: hue = (green - blue) / diff + (green < blue ? 6.0 : 0.0); break; + case 1: hue = (blue - red) / diff + 2.0; break; + case 2: hue = (red - green) / diff + 4.0; break; + default: break; + } + hue /= 6.0; + } + + dst[0] = hue; + dst[1] = saturation; + dst[2] = lightness; +} + + +static long +rgba_to_hsla (char *src, + char *dst, + long samples) +{ + long n = samples; + + while (n--) + { + double alpha = ((double *) src)[3]; + + rgb_to_hsl_step ((double *) src, (double *) dst); + + ((double *) dst)[3] = alpha; + + src += 4 * sizeof (double); + dst += 4 * sizeof (double); + } + + return samples; +} + + +static long +rgba_to_hsl (char *src, + char *dst, + long samples) +{ + long n = samples; + + while (n--) + { + rgb_to_hsl_step ((double *) src, (double *) dst); + + src += 4 * sizeof (double); + dst += 3 * sizeof (double); + } + + return samples; +} + + +static inline double +hue2cpn (double p, double q, double hue) +{ + if (hue < 0.0) hue += 1.0; + if (hue > 1.0) hue -= 1.0; + if (hue < 1.0 / 6.0) return p + (q - p) * 6.0 * hue; + if (hue < 1.0 / 2.0) return q; + if (hue < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - hue) * 6.0; + return p; +} + + +static void +hsl_to_rgb_step (double *src, + double *dst) +{ + double hue = src[0]; + double saturation = src[1]; + double lightness = src[2]; + + double red = 0, green = 0, blue = 0; + + if (saturation < 1e-7) + { + red = green = blue = lightness; + } + else + { + double q = lightness < 0.5 ? + lightness * (1 + saturation) : + lightness + saturation - lightness * saturation; + + double p = 2 * lightness - q; + + red = hue2cpn (p, q, hue + 1.0/3.0); + green = hue2cpn (p, q, hue); + blue = hue2cpn (p, q, hue - 1.0/3.0); + } + + dst[0] = gamma_2_2_to_linear (red); + dst[1] = gamma_2_2_to_linear (green); + dst[2] = gamma_2_2_to_linear (blue); +} + + +static long +hsla_to_rgba (char *src, + char *dst, + long samples) +{ + long n = samples; + + while (n--) + { + double alpha = ((double *) src)[3]; + + hsl_to_rgb_step ((double *) src, (double *) dst); + + ((double *) dst)[3] = alpha; + + src += 4 * sizeof (double); + dst += 4 * sizeof (double); + } + + return samples; +} + + +static long +hsl_to_rgba (char *src, + char *dst, + long samples) +{ + long n = samples; + + while (n--) + { + hsl_to_rgb_step ((double *) src, (double *) dst); + + ((double *) dst)[3] = 1.0; + + src += 3 * sizeof (double); + dst += 4 * sizeof (double); + } + + return samples; +} diff --git a/extensions/Makefile.am b/extensions/Makefile.am index b657117..dca87c0 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -25,6 +25,7 @@ ext_LTLIBRARIES = \ float.la \ fast-float.la \ naive-CMYK.la \ + HSL.la \ HSV.la \ simple.la \ sse2-float.la \ @@ -41,6 +42,7 @@ gggl_la_SOURCES = gggl.c gimp_8bit_la_SOURCES = gimp-8bit.c grey_la_SOURCES = grey.c naive_CMYK_la_SOURCES = naive-CMYK.c +HSL_la_SOURCES = HSL.c HSV_la_SOURCES = HSV.c sse2_float_la_SOURCES = sse2-float.c sse2_int8_la_SOURCES = sse2-int8.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 3a38e51..5f7e5b2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,6 +12,7 @@ C_TESTS = \ extract \ floatclamp \ float-to-8bit \ + hsl \ hsva \ types \ palette \ diff --git a/tests/hsl.c b/tests/hsl.c new file mode 100644 index 0000000..4b24db2 --- /dev/null +++ b/tests/hsl.c @@ -0,0 +1,111 @@ +/* babl - dynamically extendable universal pixel conversion library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see + * . + */ + +#include +#include + +#include "config.h" +#include "babl.h" + +#define CHECK_CONV(test_name, componenttype, src_fmt, dst_fmt, src_pix, expected_pix) \ + { \ + const Babl *fish; \ + int i; \ + fish = babl_fish (src_fmt, dst_fmt); \ + if (!fish) \ + { \ + printf (" %s failed to make fish\n", test_name); \ + OK = 0; \ + } \ + for (i = 0; i < sizeof(src_pix)/sizeof(src_pix[0]); i ++) \ + { \ + int c; \ + componenttype result[10]; \ + babl_process (fish, src_pix[i], result, 1); \ + for (c = 0; c < sizeof(expected_pix[i])/sizeof(expected_pix[i][0]); c++) \ + if (fabs(result[c] - expected_pix[i][c]) > 0.001) \ + { \ + printf (" %s failed #%i[%i] got %lf expected %lf\n", \ + test_name, i, c, result[c], expected_pix[i][c]); \ + OK = 0; \ + } \ + } \ + } + + +int +main (int argc, + char **argv) +{ + int OK = 1; + + float rgba[][4] = {{1.0 , 1.0 , 1.0 , 1.0}, + {0.214041, 0.214041, 0.214041, 1.0}, + {0.0 , 0.0 , 0.0 , 1.0}, + {1.0 , 0.0 , 0.0 , 1.0}, + {0.522519, 0.522519, 0.0 , 1.0}, + {0.0 , 0.214041, 0.0 , 1.0}, + {0.214041, 1.0 , 1.0 , 1.0}, + {0.214041, 0.214041, 1.0 , 1.0}, + {0.522520, 0.050876, 0.522521, 1.0}, + {0.353069, 0.372000, 0.017878, 1.0}, + {0.052772, 0.010679, 0.823194, 1.0}, + {0.012693, 0.414530, 0.052934, 1.0}, + {0.870621, 0.579515, 0.004228, 1.0}, + {0.453672, 0.029212, 0.781390, 1.0}, + {0.850554, 0.181933, 0.081839, 1.0}, + {0.995195, 0.941644, 0.244979, 1.0}, + {0.009836, 0.595745, 0.308242, 1.0}, + {0.036595, 0.019338, 0.315257, 1.0}, + {0.209470, 0.207646, 0.478434, 1.0}}; + + float hsla[][4] = {{0.0, 0.0 , 1.0 , 1.0}, + {0.0, 0.0 , 0.500, 1.0}, + {0.0, 0.0 , 0.0 , 1.0}, + {0.0, 1.0 , 0.500, 1.0}, + {0.166667, 1.0 , 0.375, 1.0}, + {0.333333, 1.0 , 0.250, 1.0}, + {0.5, 1.0 , 0.750, 1.0}, + {0.666666, 1.0 , 0.750, 1.0}, + {0.833333, 0.500, 0.500, 1.0}, + {0.171666, 0.638, 0.393, 1.0}, + {0.6975, 0.832, 0.511, 1.0}, + {0.374722, 0.707, 0.396, 1.0}, + {0.1375, 0.893, 0.497, 1.0}, + {0.788028, 0.775, 0.542, 1.0}, + {0.039837, 0.817, 0.624, 1.0}, + {0.158083, 0.991, 0.765, 1.0}, + {0.451149, 0.779, 0.447, 1.0}, + {0.689732, 0.601, 0.373, 1.0}, + {0.668129, 0.290, 0.607, 1.0}}; + + babl_init (); + + CHECK_CONV ("rgba to hsla ", float, + babl_format ("RGBA float"), + babl_format ("HSLA float"), + rgba, hsla); + + CHECK_CONV ("hsla to rgba ", float, + babl_format ("HSLA float"), + babl_format ("RGBA float"), + hsla, rgba); + + babl_exit (); + + return !OK; +} -- 2.30.2